package com.sharemedia.views
{
	import com.sharemedia.skins.defaultSkin.SoundVisualizerSkin;
	import com.sharemedia.utils.SkinUtil;
	
	import flash.events.Event;
	import flash.media.SoundMixer;
	import flash.utils.ByteArray;
	
	import mx.core.IVisualElement;
	import mx.effects.easing.Quadratic;
	import mx.styles.CSSStyleDeclaration;
	import mx.styles.StyleManager;
	
	import spark.components.Group;
	import spark.components.supportClasses.SkinnableComponent;

	/**
	 * Class that represents visually snapshot of current played sound.  
	 */	
	public class SoundVisualization extends SkinnableComponent
	{
		
		// Set default skin
		SkinUtil.setSkin("com.sharemedia.views.SoundVisualization", SoundVisualizerSkin);
		
		/**
		 * Constructor. 
		 */		
		public function SoundVisualization()
		{
			super();
		}
		
		[SkinPart(required="true")]
		/**
		 *  A skin part that defines the stage where visualization is placed. 
		 */
		public var visualizationStage:Group;
		
		/**
		 * Flag thet indicates whether enter frame listener is added. 
		 */		
		private var enterFrameListenerAdded:Boolean = false;
		
		/**
		 * Play visualization. 
		 */		
		public function play():void
		{
			if(!enterFrameListenerAdded)
			{
				systemManager.addEventListener(Event.ENTER_FRAME, endterFrameHandler);
				enterFrameListenerAdded = true;
			}
		}
		
		/**
		 * Stop visualization. 
		 */	
		public function stop():void
		{
			if(enterFrameListenerAdded)
			{
				systemManager.removeEventListener(Event.ENTER_FRAME, endterFrameHandler);
				enterFrameListenerAdded = false;
			}
		}
		
		/**
		 * Previous list of sound visual segments.
		 */		
		private var previousSegments:Vector.<Number>;
		
		/**
		 * Handler for enter frame event.
		 *  
		 * @param event Event
		 */		
		private function endterFrameHandler(event:Event):void
		{
			if(SoundMixer.areSoundsInaccessible())
				return;
			
			var bytes : ByteArray = new ByteArray();
			// Take a picture of the audio data that's playing
			SoundMixer.computeSpectrum(bytes, true, 1);
			
			var leftSegments:Vector.<Number> = extractFFT(bytes);
			var rightSegments:Vector.<Number> = extractFFT(bytes);
		
			var combinedSegments:Vector.<Number> = new Vector.<Number>();
			for (var i:int=0; i<leftSegments.length; i++)
			{
				var everage:Number = (leftSegments[i]+rightSegments[i])/2;
				if(everage > 1)
					everage = 1;
				everage = Quadratic.easeOut(everage, 0, 1, 1);
				
				if(previousSegments)
				{
					// Apply some easing to reduce the everage so
					// that it approaches 0.
					var easedValue:Number = previousSegments[i] - previousSegments[i] / 4;
					
					if(everage < easedValue) {
						everage = easedValue; 
					}
					
					if(everage < 0)
						everage = 0;
				}
				combinedSegments.push(everage);
			}
			
			previousSegments = combinedSegments;
			
			visualize(combinedSegments);
		}
		
		/**
		 * Read bytes and calculates list of segments for one chanel.
		 *  
		 * @param bytes ByteArray
		 * @return  Vector.<Number>
		 */		
		public function extractFFT(bytes:ByteArray):Vector.<Number>
		{
			var segments:Vector.<Number> = new Vector.<Number>();
			
			var valuesPerSegment:Number = Math.floor(256/visualizationStage.numElements);
			for (var segmentCounter:int = 0; segmentCounter < visualizationStage.numElements; segmentCounter++) 
			{
				var segmentTotals:Number = 0;
				
				var valuesInSegment:Number = valuesPerSegment;
				if(segmentCounter == visualizationStage.numElements-1)
					valuesInSegment = (256 - valuesPerSegment*(visualizationStage.numElements-1));
				
				for (var i:int = 0; i < valuesInSegment; i++) 
				{
					segmentTotals += Math.abs(bytes.readFloat());
				}
				
				var everage:Number = segmentTotals/valuesInSegment;
				segments.push(everage);
			}
			
			return segments;
		}
		
		/**
		 * Sets size of visual segments. 
		 * 
		 * @param segments Vector.<Number>
		 */		
		private function visualize(segments:Vector.<Number>):void
		{
			for (var i:int = 0; i < segments.length; i++)
			{
				var element:IVisualElement = visualizationStage.getElementAt(i);
				element.height = segments[i]*visualizationStage.getLayoutBoundsHeight();
			}
		}
	}
}